package drds.data_propagate.driver.packets;

import drds.data_propagate.driver.utils.ByteHelper;
import lombok.Getter;
import lombok.Setter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;


public class UuidSet {
    @Setter
    @Getter
    public UUID sid;
    @Setter
    @Getter
    public List<Interval> intervalList;

    /**
     * 解析如下格式字符串为UUIDSet: 726757ad-4455-11e8-ae04-0242ac110002:1 => UuidSet{sid:
     * 726757ad-4455-11e8-ae04-0242ac110002, intervalList: [{start:1, stop:2}]}
     * 726757ad-4455-11e8-ae04-0242ac110002:1-3 => UuidSet{sid:
     * 726757ad-4455-11e8-ae04-0242ac110002, intervalList: [{start:1, stop:4}]}
     * 726757ad-4455-11e8-ae04-0242ac110002:1-3:4 UuidSet{sid:
     * 726757ad-4455-11e8-ae04-0242ac110002, intervalList: [{start:1, stop:5}]}
     * 726757ad-4455-11e8-ae04-0242ac110002:1-3:7-9 UuidSet{sid:
     * 726757ad-4455-11e8-ae04-0242ac110002, intervalList: [{start:1, stop:4},
     * {start:7, stop:10}]}
     */
    public static UuidSet parse(String string) {
        String[] splits = string.split(":");

        if (splits.length < 2) {
            throw new RuntimeException(String.format("parseUUIDSet failed due to wrong format: %s", string));
        }

        List<Interval> intervalList = new ArrayList<Interval>();
        for (int i = 1; i < splits.length; i++) {
            intervalList.add(parseInterval(splits[i]));
        }

        UuidSet uuidSet = new UuidSet();
        uuidSet.sid = UUID.fromString(splits[0]);
        uuidSet.intervalList = combine(intervalList);

        return uuidSet;
    }

    /**
     * 解析如下格式字符串为Interval: 1 => Interval{start:1, stop:2} 1-3 => Interval{start:1,
     * stop:4} 注意！字符串格式表达时[n,m]是两侧都包含的，Interval表达时[n,m)右侧开
     */
    public static Interval parseInterval(String string) {
        String[] splits = string.split("-");

        Interval interval = new Interval();
        switch (splits.length) {
            case 1:
                interval.start = Long.parseLong(splits[0]);
                interval.stop = interval.start + 1;
                break;
            case 2:
                interval.start = Long.parseLong(splits[0]);
                interval.stop = Long.parseLong(splits[1]) + 1;
                break;
            default:
                throw new RuntimeException(String.format("parseInterval failed due to wrong format: %s", string));
        }

        return interval;
    }

    /**
     * 把{start,stop}连续的合并掉: [{start:1, stop:4},{start:4, stop:5}] => [{start:1,
     * stop:5}]
     */
    public static List<Interval> combine(List<Interval> intervalList) {
        List<Interval> combined = new ArrayList<Interval>();
        Collections.sort(intervalList);
        int size = intervalList.size();
        for (int i = 0; i < size; i++) {
            combined.add(intervalList.get(i));

            int j;
            for (j = i + 1; j < size; j++) {
                if (intervalList.get(i).stop >= intervalList.get(j).start) {
                    intervalList.get(i).stop = intervalList.get(j).stop;
                } else {
                    break;
                }
            }
            i = j - 1;
        }

        return combined;
    }

    public byte[] encode() throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
        byteBuffer.putLong(sid.getMostSignificantBits());
        byteBuffer.putLong(sid.getLeastSignificantBits());

        byteArrayOutputStream.write(byteBuffer.array());

        ByteHelper.writeUnsignedInt64LittleEndian(intervalList.size(), byteArrayOutputStream);

        for (Interval interval : intervalList) {
            ByteHelper.writeUnsignedInt64LittleEndian(interval.start, byteArrayOutputStream);
            ByteHelper.writeUnsignedInt64LittleEndian(interval.stop, byteArrayOutputStream);
        }

        return byteArrayOutputStream.toByteArray();
    }

    @Override
    public boolean equals(Object object) {
        if (object == null)
            return false;
        if (this == object)
            return true;

        UuidSet uuidSet = (UuidSet) object;
        Collections.sort(intervalList);
        Collections.sort(uuidSet.intervalList);
        if (sid.equals(uuidSet.sid) && intervalList.equals(uuidSet.intervalList)) {
            return true;
        }

        return false;
    }


    public static class Interval implements Comparable<Interval> {

        public long start;
        public long stop;

        @Override
        public boolean equals(Object object) {
            if (this == object)
                return true;
            if (object == null || getClass() != object.getClass())
                return false;

            Interval interval = (Interval) object;

            if (start != interval.start)
                return false;
            return stop == interval.stop;
        }

        @Override
        public int hashCode() {
            int result = (int) (start ^ (start >>> 32));
            result = 31 * result + (int) (stop ^ (stop >>> 32));
            return result;
        }


        public int compareTo(Interval interval) {
            if (equals(interval)) {
                return 1;
            }
            return start > interval.start ? 1 : (start == interval.start ? 0 : -1);
        }
    }
}
